﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Medhurst.Unity.Configuration
{
    /// <summary>
    /// Converts <see cref="NameValueCollection"/>s to/from strings using the syntax:
    /// <code>Key1=Value1;Key2=Value #2</code>
    /// </summary>
    public class KeyValueConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var val = value as String;
            if (val == null) return new NameValueCollection();

            return ParseString(val);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var sb = new StringBuilder();
            if (value == null) return "";
            var col = value as NameValueCollection;
            Debug.Assert(col != null, "ConvertTo bug?");
            foreach (var key in col.Keys.Cast<string>())
                sb.AppendFormat("{0}={1};", key, col[key]);
            return sb.ToString().TrimEnd(';');
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(string);
        }

        public static NameValueCollection ParseString(string input)
        {
            var result = new NameValueCollection();
            var buffer = new StringBuilder();
            bool inValue = false;
            bool isFirstChar = true;
            bool withinBrackets = false;
            string propertyName = null;
            var value = "";
            foreach (char c in input)
            {
                // prop/value splitter
                if (c == '=' && !inValue && !isFirstChar)
                {
                    propertyName = buffer.ToString();
                    buffer.Clear();
                    inValue = true;
                    continue;
                }

                // pair splitter
                if (c == ';' && inValue && !withinBrackets)
                {
                    value = buffer.ToString();
                    buffer.Clear();
                    inValue = false;
                    isFirstChar = true;
                    result.Add(propertyName, value);
                    continue;
                }

                // brackets escapes all = and ; symbols
                if (c == '(' && inValue)
                {
                    withinBrackets = true;
                }
                else if (c == ')' && inValue && withinBrackets)
                {
                    withinBrackets = false;
                }

                // append character
                buffer.Append(c);
                isFirstChar = false;
            }

            // end of string, keep whatever you have already got
            if (inValue)
                result.Add(propertyName, buffer.ToString());
            else if (buffer.Length > 0)
                result.Add(buffer.ToString(), "");

            return result;
        }
    }
}
